package org.ma1.cripto.aes.ppal;

import org.ma1.cripto.aes.comun.Bloque;
import org.ma1.cripto.aes.comun.Clave;
import org.ma1.cripto.aes.comun.Texto;
import org.ma1.cripto.aes.operacion.ExtendKey;

public class Manager {
	
	public static final int MODO_CIFRAR = 0;
	public static final int MODO_DESCIFRAR = 1;
	
	/* Para cifrar/descifrar */
	private IState estado;
	public int mode;
	
	/* Para procesar todos los bloques */
	private int indiceBloqueTextoEntrada = 0;
	
	/* Para gestionar estado del algoritmo */
	private Texto textoEntrada, textoResultado;
	private int indice = 0;

	public Manager() {
		setMode(MODO_CIFRAR);
	}

	public int getMode() {
		return mode;
	}

	public void setMode(int mode){
		this.mode = mode;
		if (mode == MODO_CIFRAR)
			estado = new CifrarState();
		else if (mode == MODO_DESCIFRAR) {
			estado = new DescifrarState();
		} 
	}

	public void ejecutar() throws Exception {
		while (!this.nextStep());
	}

	public boolean nextStep() {
		boolean finalizadoAlgoritmo = false;
		Bloque aux = estado.doStep(indice++, textoResultado.getBloques().get(indiceBloqueTextoEntrada));
		if (aux!=null) {
			textoResultado.getBloques().set(indiceBloqueTextoEntrada, aux);
		} else {
			// No hay operacion
			indiceBloqueTextoEntrada++;
			finalizadoAlgoritmo = indiceBloqueTextoEntrada>=textoResultado.getBloques().size();
			indice = 0;
			estado.nuevaRonda();
		}
		return finalizadoAlgoritmo;
	}

	public void setTextoClave(Texto texto, Clave clave) {
		this.textoEntrada = texto;
		this.textoResultado = texto;
		this.estado.setRondas(obtenerNumRondas(clave), calcularClavesDeRonda(clave));
	}

	private Clave[] calcularClavesDeRonda(Clave clave) {
		int numRondas = obtenerNumRondas(clave);
		Clave[] claves = new Clave[numRondas+1];
		
		ExtendKey ext = new ExtendKey(
				numRondas,
				clave.getTamBloque() / 4,
				((Bloque) this.textoEntrada.getBloques().get(0)).getTamBloque() / 4);
		byte[] c = new byte[clave.getTamBloque() * (numRondas + 1)];

		Clave c2 = ((Clave) (clave.clone()));
		ext.KeyExpansion(c2, c);
		
		int k=0;
		for (int i=0; i<numRondas+1; i++) {
			claves[i] = new Clave();
			claves[i].setTamBloque(clave.getTamBloque());
			for (int j=0; j<clave.getTamBloque(); j++) {
				claves[i].setByte(j,c[k++]);
			}
		}
		
		return claves;
	}

	private int obtenerNumRondas(Clave c) {
		int rondas = 0;
		int Nk = c.getTamBloque();
		int Nb = Bloque.N;

		if ((Nk == 16) && (Nb == 16))
			rondas = 10;
		else {
			if (((Nk == 16) && (Nb == 24)) || ((Nk == 24) && (Nb == 24))
					|| ((Nb == 16) && (Nk == 24)))
				rondas = 12;
			else {
				rondas = 14;
			}
		}
		return rondas;

	}
	
	public void setEstado(IState estado) {
		this.estado = estado;
	}


	public String getNombreOperacionActual() {
		return estado.getNombreOperacion(indice-1);
	}

	public String getNombreSiguienteOperacion() {
		return estado.getNombreOperacion(indice);
	}
	
	public Texto getTextoEntrada() {
		return textoEntrada;
	}
	
	public Texto getTextoResultado() {
		return textoResultado;
	}
}
